19 Seaborn库基础
19.1 引言Seaborn的优势
Seaborn建立在Matplotlib之上:
- 更美观: 默认样式更好
- 更简洁: 更少代码实现复杂图表
- 统计图表: 专为统计设计
19.2 本章学习目标
通过本章学习,你将能够:
- 说出 Seaborn 与 Matplotlib 的关系:前者是构建在后者之上的统计图表高层接口,配置最终仍落到
plt.rcParams与 Axes 上 - 区分图形级函数(
catplot,自建画布、返回 FacetGrid)与坐标轴级函数(barplot、boxplot、regplot等,可用ax=嵌入子图) - 用
sns.load_dataset()加载内置数据集,完成分组统计图并解读误差线(95% 置信区间) - 用
set_style()、set_palette()设置全局样式与配色,并说明其作用范围 - 用
heatmap可视化相关系数矩阵,为第21章多维相关性分析做铺垫
先修:章节 18、章节 10(第18章 Matplotlib 基础与第10章 Pandas 数据框:Seaborn 的绘图数据以 DataFrame 为主,图形要素仍用 Matplotlib 概念理解)
19.3 泰坦尼克数据集分析
任务要求(归纳自块内任务注释与代码):加载 Seaborn 内置的 titanic 数据集;用 sns.catplot 绘制以 class 为 x 轴、survived 为 y 轴、按 sex 分色的分组柱状图(kind="bar");调整子图顶部边距(subplots_adjust(top=0.9))后为整幅图添加总标题 "Survival rate by class and sex";将图形保存为 1.png。请将代码原样输入教学平台(注释除外),判定以平台为准。
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
import seaborn as sns # 导入Seaborn可视化库
import matplotlib.pyplot as plt # 导入Matplotlib绘图库
# 加载titanic数据集
titanic = sns.load_dataset("titanic")
# 使用catplot函数绘制柱状图
sns.catplot(x="class", y="survived", hue="sex", data=titanic, kind="bar")
# 设置标题
plt.subplots_adjust(top=0.9)
plt.suptitle("Survival rate by class and sex") # 设置图形总标题
# 显示图形
plt.savefig("1.png")预期输出(本机 Python 实际运行结果,具体以平台运行结果为准):控制台无输出;生成文件 1.png,为一张按舱位等级分面、按性别分色的分组柱状图,共 6 根柱子,每根柱顶带一根黑色误差线(95% 置信区间)。数据集共 891 条乘客记录,各柱高度(生还率,保留两位小数)为:一等舱女性 0.97、男性 0.37;二等舱女性 0.92、男性 0.16;三等舱女性 0.50、男性 0.14——女性生还率系统性高于男性,且随舱位等级下降而下降,三等舱男性生还率最低。本机实跑已验证上述数值;load_dataset 首次运行需联网下载数据集。
19.4 常用Seaborn图表
# =============================================================================
# 题目:Seaborn四种常用统计图表的综合演示
# =============================================================================
# 本代码展示Seaborn的核心图表类型:箱线图(展示分布和异常值)、小提琴图
# (结合箱线图和核密度估计)、热力图(展示相关性矩阵)和散点图带回归线
# (展示变量关系)。这些图表是数据分析和统计建模中的基础工具。
# ==================== 导入必要的库 ====================
import numpy as np # 导入NumPy库,用于生成随机数
import pandas as pd # 导入Pandas库,用于数据处理
# ==================== 创建示例数据 ====================
# 设置随机种子,确保每次运行结果相同
np.random.seed(42) # 42是常用的随机种子,保证代码可重复
# 创建包含100行数据的DataFrame
data = pd.DataFrame({
# Category列:从A、B、C三个类别中随机抽取100次,生成分类变量
'Category': np.random.choice(['A', 'B', 'C'], 100),
# Value1列:生成100个服从标准正态分布的随机数(均值0,标准差1)
'Value1': np.random.randn(100),
# Value2列:生成100个标准正态分布随机数后加2,使其均值变为2
'Value2': np.random.randn(100) + 2 # 这使得Value2整体大于Value1
})
# ==================== 创建2x2的子图布局 ====================
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
# 创建画布和子图数组,总尺寸12x10英寸
# ==================== 子图1:箱线图 ====================
# 箱线图展示数据的分布特征:中位数、四分位数、异常值
sns.boxplot(data=data, x='Category', y='Value1', ax=axes[0, 0])
# x参数指定分类变量(横轴)
# y参数指定连续变量(纵轴)
# 箱线图从下到上依次表示:最小值、Q1(25%)、中位数、Q3(75%)、最大值
# 超出1.5倍IQR(四分位距)的点会被标记为异常值
# 设置子图1标题
axes[0, 0].set_title('箱线图') # 箱线图能快速识别数据分布和异常值
# ==================== 子图2:小提琴图 ====================
# 小提琴图是箱线图和核密度图的结合,更直观地展示分布形状
sns.violinplot(data=data, x='Category', y='Value1', ax=axes[0, 1])
# 参数含义与boxplot相同,但视觉效果更丰富
# 小提琴的宽度表示数据在该值的密度,越宽表示出现频率越高
# 设置子图2标题
axes[0, 1].set_title('小提琴图') # 能看到分布的对称性、峰度等特征
# ==================== 子图3:热力图 ====================
# 计算Value1和Value2的相关系数矩阵
corr = data[['Value1', 'Value2']].corr()
# corr()方法计算皮尔逊相关系数,衡量两个变量的线性相关程度
# 返回一个2x2的对称矩阵,对角线为1(自相关),非对角线为互相关
# 使用热力图可视化相关系数矩阵
sns.heatmap(corr, annot=True, cmap='coolwarm', ax=axes[1, 0])
# annot=True表示在热力图的每个格子上显示数值
# cmap='coolwarm'指定颜色方案(冷色表示负相关,暖色表示正相关)
# 热力图用颜色深浅表示相关系数大小,一目了然
# 设置子图3标题
axes[1, 0].set_title('相关性热力图') # 展示变量间的线性关系强度
# ==================== 子图4:散点图带回归线 ====================
# regplot结合散点图和线性回归拟合,展示变量关系和趋势
sns.regplot(data=data, x='Value1', y='Value2', ax=axes[1, 1])
# x参数指定自变量(横轴)
# y参数指定因变量(纵轴)
# 自动计算并绘制线性回归线,以及95%置信区间(半透明阴影)
# 设置子图4标题
axes[1, 1].set_title('散点图与回归线') # 直观判断变量关系和拟合优度
# ==================== 调整布局并显示 ====================
plt.tight_layout() # 自动调整子图间距
plt.show() # 显示完整的四合一图表
# 输出解读:
# 图1的箱线图展示了三个类别数据的分布特征和异常值
# 图2的小提琴图更细致地显示了分布形状,可能发现多峰分布等特征
# 图3的热力图显示Value1和Value2的相关系数(接近0表示无相关)
# 图4的散点图和回归线直观展示了两个变量的线性关系强度19.5 样式设置
# =============================================================================
# 题目:Seaborn样式和配色方案的自定义
# =============================================================================
# 本代码演示如何设置Seaborn的全局样式和配色方案,使图表更美观、更符合
# 个性化需求。Seaborn提供了5种内置样式和多种配色方案,适合不同的场景
# 和数据类型。
# ==================== 设置Seaborn样式 ====================
# set_style()设置图表的整体风格,影响背景、网格、坐标轴等元素
sns.set_style("whitegrid") # 使用白色背景+灰色网格,适合大多数数据可视化场景
# 可选样式:
# "whitegrid": 白色背景,带网格(推荐用于数据展示)
# "darkgrid": 深色背景,带网格(适合深色主题)
# "white": 纯白背景,无网格(简洁风格)
# "dark": 深色背景,无网格(护眼模式)
# "ticks": 白色背景,仅保留刻度线(学术风格)
# ==================== 设置配色方案 ====================
# set_palette()设置图表的颜色循环顺序,影响所有使用颜色的图表元素
sns.set_palette("husl") # 使用HUSL配色方案,颜色鲜艳且区分度高
# 常用配色方案:
# "husl": 均匀分布的色彩空间,适合分类数据
# "pastel": 柔和的粉彩色系
# "dark": 深色调
# "colorblind": 色盲友好的配色
# "Set1", "Set2": matplotlib的内置配色
# ==================== 创建散点图 ====================
# 创建一个新的画布,尺寸为10x6英寸
plt.figure(figsize=(10, 6)) # 单独创建画布,不使用子图
# 加载Seaborn内置的小费数据集(餐厅顾客的小费记录)
data_plot = sns.load_dataset('tips')
# 该数据集包含total_bill(总账单)、tip(小费)、day(星期)、size(人数)等字段
# 绘制多维散点图,通过颜色和大小区分不同维度
sns.scatterplot(data=data_plot, x='total_bill', y='tip', hue='day', size='size')
# x参数指定横轴变量(总账单金额)
# y参数指定纵轴变量(小费金额)
# hue参数指定按星期着色,不同星期用不同颜色表示
# size参数指定按人数映射点的大小,人数越多点越大
# ==================== 设置图表标签和标题 ====================
plt.title('餐厅小费分析', fontsize=14) # 设置图表标题,字体大小14
plt.xlabel('总账单', fontsize=12) # 设置横轴标签,字体大小12
plt.ylabel('小费', fontsize=12) # 设置纵轴标签,字体大小12
# 设置图例
plt.legend(title='星期') # 图例标题设为"星期",显示每天的标识
# ==================== 显示图表 ====================
plt.show() # 展示完整的散点图
# 输出解读:
# 散点图展示了总账单与小费金额的正相关关系
# - 不同颜色(星期)的点的分布可能有差异,暗示周末小费比例不同
# - 点的大小(用餐人数)也影响小费金额,人数多的小费绝对值更高
# - 使用whitegrid样式和husl配色,图表清晰美观,便于识别模式和异常值19.6 本章小结
要点:
- Seaborn 建立在 Matplotlib 之上,用更少的代码得到更美观的统计图表;它接受整表数据(
data=传 DataFrame、x=/y=/hue=传列名),这是与 Matplotlib 逐条传数据最大的用法差异 - 函数分两级:图形级(如
catplot)自己创建画布、返回 FacetGrid 对象,适合快速分面;坐标轴级(如boxplot、violinplot、heatmap、regplot、scatterplot)接受ax=参数,可与plt.subplots组合嵌入子图 catplot(kind="bar")自动完成分组均值统计,柱高是各组的y均值,并附带 95% 置信区间误差线——它画的是统计量,不是原始数据set_style()管背景与网格(whitegrid/darkgrid/white/dark/ticks),set_palette()管配色;两者都是全局设置,会波及之后的所有图sns.heatmap(corr, annot=True, cmap=...)是相关系数矩阵的标准可视化方式,是第21章热力图任务的基础
易错点:
- 图形级函数(如
catplot)没有ax=参数,无法嵌入已有子图;要嵌入须改用坐标轴级等价函数(如sns.barplot) set_style/set_palette是全局的,忘记这一点会让后面的 Matplotlib 图也被”顺带”改了样式- 平台代码的
plt.suptitle设置的是整幅图的总标题,与子图标题(set_title)不在一个层级,需要subplots_adjust(top=0.9)留出空间,否则会与子图重叠 load_dataset的数据集名写错或断网时下载失败会报错;首次下载后有本地缓存,之后离线可用- 柱状图的误差线是置信区间,长短同时取决于样本量与该组比例的方差 \(p(1-p)\)(\(p\) 越接近 0.5 方差越大),样本多的组误差线未必更短,解读时不能只看柱高
19.7 动手与思考
以下练习每题附参考答案(默认折叠)。请先独立完成并写下你的判断,再点开对照,最后上机验证。
输出预测:不运行代码,先写出下面代码的输出结果,再上机检验你的判断。
import seaborn as sns import matplotlib.pyplot as plt titanic = sns.load_dataset("titanic") g = sns.catplot(x="class", y="survived", data=titanic, kind="bar") print(type(g).__name__) print(len(g.fig.axes)) fig, ax = plt.subplots() sns.boxplot(x="class", y="age", data=titanic, ax=ax) print(type(ax).__name__)参考答案(先写下你的预测再点开)
解题思路:逐行推演。
sns.catplot是图形级函数,自己创建画布并返回 FacetGrid 对象,故第 1 个print输出FacetGrid;本题没有col/row分面,整幅图只有 1 个坐标轴,g.fig.axes是该图全部坐标轴的列表,故第 2 个print输出1。sns.boxplot是坐标轴级函数,接受ax=参数嵌入指定坐标轴;plt.subplots()不带行列参数时返回单个 Axes 对象,第 3 个print输出其类型名。注意版本差异:matplotlib 3.8 起type(ax).__name__输出Axes,旧版本(如 3.5)输出AxesSubplot,以你所用版本的实际输出为准;本机 peter 环境为 matplotlib 3.10,输出Axes。此外sns.load_dataset("titanic")首次运行需联网下载数据集,之后有本地缓存可离线使用。# 验证脚本:逐行输出三个表达式的求值结果(titanic首次加载需联网) import seaborn as sns # 导入Seaborn可视化库 import matplotlib.pyplot as plt # 导入Matplotlib绘图库 titanic = sns.load_dataset("titanic") # 加载Seaborn内置数据集 g = sns.catplot(x="class", y="survived", data=titanic, kind="bar") # 图形级函数:自建画布 print(type(g).__name__) # catplot返回对象的类型名 print(len(g.fig.axes)) # FacetGrid画布上坐标轴的个数 fig, ax = plt.subplots() # 单个子图:返回单个Axes对象而非数组 sns.boxplot(x="class", y="age", data=titanic, ax=ax) # 坐标轴级函数:嵌入指定坐标轴 print(type(ax).__name__) # 该坐标轴对象的类型名预期输出(本机 peter 环境实际运行结果,titanic 首次加载需联网,具体以平台运行结果为准):
FacetGrid 1 Axes回扣本章:对应本章小结“要点”第 2 条与“易错点”第 1 条——图形级函数(
catplot)自建画布、返回 FacetGrid 且没有ax=参数;坐标轴级函数(如boxplot)接受ax=可嵌入子图。概念辨析:图形级函数与坐标轴级函数各适合什么场景?想在一张 1×2 的子图里并排画两张 Seaborn 图,应选哪一级、为什么?(
catplot能否通过ax=传入?可用inspect.signature检查。)参考答案(点开前请先独立完成)
解题思路:图形级函数(
catplot、relplot、displot等)自己创建画布并返回 FacetGrid,适合快速按col/row分面画一排图;坐标轴级函数(barplot、boxplot、violinplot、heatmap、regplot、scatterplot等)接受ax=参数,可与plt.subplots组合嵌入指定子图。想在一张 1×2 的子图里并排画两张 Seaborn 图,应选坐标轴级函数:先fig, axes = plt.subplots(1, 2),再分别传ax=axes[0]、ax=axes[1],两图共享同一画布便于统一尺寸与导出。catplot不能通过ax=传入——其函数签名中没有ax参数(可用inspect.signature查证,下方代码输出False),它必须自建画布;要嵌入已有子图须改用坐标轴级等价函数(如sns.barplot)。# 验证脚本:检查两级函数的参数签名中是否含ax import inspect # 导入检查函数签名的标准库模块 import seaborn as sns # 导入Seaborn可视化库 print('catplot 有 ax 参数:', 'ax' in inspect.signature(sns.catplot).parameters) # 图形级函数签名检查 print('barplot 有 ax 参数:', 'ax' in inspect.signature(sns.barplot).parameters) # 坐标轴级函数签名检查 print('boxplot 有 ax 参数:', 'ax' in inspect.signature(sns.boxplot).parameters) # 坐标轴级函数签名检查预期输出(本机 peter 环境实际运行结果,具体以平台运行结果为准):
catplot 有 ax 参数: False barplot 有 ax 参数: True boxplot 有 ax 参数: True回扣本章:对应本章小结“要点”第 2 条与“易错点”第 1 条——图形级函数没有
ax=参数、无法嵌入已有子图,要嵌入须改用坐标轴级等价函数。变式任务(平台任务同型改造):把平台任务的
catplot(kind="bar")改为坐标轴级函数sns.barplot绘制同样的 class×sex 生还率图并嵌入plt.subplots创建的坐标轴;再画一张embark_town×sex的生还率图与之并排,对比两张图的样本量(误差线长短)差异。参考答案(点开前请先独立完成)
解题思路:平台任务(列表 19.1)用的是图形级
catplot(kind="bar")自建画布;变式改用坐标轴级sns.barplot,先plt.subplots(1, 2)创建两个坐标轴,再分别以ax=axes[0]、ax=axes[1]嵌入class×sex与embark_town×sex两张生还率图。注意embark_town有 2 个缺失值,画图时按成对完备样本自动剔除,该图各组人数按 889 人分配。误差线对比的读法:embark_town图各分组样本普遍更小(36—441 人),其中昆士敦(Queenstown)女性组仅 36 人、误差线最长;南安普敦(Southampton)男性 441 人、误差线最短;class图中三等舱男性 347 人的误差线也很短——可见误差线长短由样本量与组内方差共同决定,样本多的组误差线未必一定更短,读图不能只看柱高。# 变式程序:用坐标轴级barplot把两张生还率图嵌入并排子图 import seaborn as sns # 导入Seaborn可视化库 import matplotlib.pyplot as plt # 导入Matplotlib绘图库 titanic_df = sns.load_dataset('titanic') # 加载Seaborn内置数据集(首次运行需联网) fig, axes = plt.subplots(1, 2, figsize=(12, 4)) # 创建1行2列子图 sns.barplot(x='class', y='survived', hue='sex', data=titanic_df, ax=axes[0]) # 舱位×性别生还率嵌入左子图 axes[0].set_title('class × sex 生还率') # 左子图标题 sns.barplot(x='embark_town', y='survived', hue='sex', data=titanic_df, ax=axes[1]) # 登船港口×性别生还率嵌入右子图 axes[1].set_title('embark_town × sex 生还率') # 右子图标题 plt.tight_layout() # 自动调整布局 plt.savefig('ch19_q3.png') # 保存图形(也可用plt.show()显示) class_table = titanic_df.groupby(['class', 'sex'], observed=True)['survived'].agg(['count', 'mean']).round(3) # 左图各组人数与生还率 town_table = titanic_df.groupby(['embark_town', 'sex'], observed=True)['survived'].agg(['count', 'mean']).round(3) # 右图各组人数与生还率 print('class × sex 各组人数与生还率:') # 打印左图底表 print(class_table) # 输出左图底表 print('embark_town × sex 各组人数与生还率:') # 打印右图底表 print(town_table) # 输出右图底表预期输出(本机 peter 环境实际运行结果,titanic 首次加载需联网,图形在线版不执行、以教学平台渲染为准):
class × sex 各组人数与生还率: count mean class sex First female 94 0.968 male 122 0.369 Second female 76 0.921 male 108 0.157 Third female 144 0.500 male 347 0.135 embark_town × sex 各组人数与生还率: count mean embark_town sex Cherbourg female 73 0.877 male 95 0.305 Queenstown female 36 0.750 male 41 0.073 Southampton female 203 0.690 male 441 0.175图形判读:两张图各 6 根柱(3 个分组 × 2 个性别),每根柱顶带一根黑色 95% 置信区间误差线;本机固定随机种子的自助抽样实测,右图昆士敦女性组(36 人)误差线半长约 0.14、最长,南安普敦男性组(441 人)半长约 0.03、最短(自助抽样每次运行略有波动,量级不变)。
注意:以上为本题变式的独立代码;列表 19.1 对应平台任务的原始代码块仍须原样输入教学平台,不要用本变式替换。
回扣本章:对应本章小结“要点”第 2、3 条(坐标轴级函数接受
ax=;kind="bar"的柱高是分组均值并附 95% 置信区间误差线)与“易错点”第 5 条(误差线长短同时取决于样本量与组内方差)。思考题:柱高是各组生还率的均值,误差线是 95% 置信区间。一等舱男性乘客(122 人)比一等舱女性(94 人)样本更多,但其柱子的误差线反而明显更长。为什么?提示:生还率这类比例变量的方差为 \(p(1-p)\),在 \(p=0.5\) 附近最大。这对”读图下结论”有什么提醒?
参考答案(点开前请先独立完成)
解题思路:生还率是比例变量,某组比例估计的标准误为 \(\sqrt{p(1-p)/n}\),其中 \(p(1-p)\) 是比例的方差、在 \(p=0.5\) 附近最大、越接近 0 或 1 越小。一等舱男性 \(p\approx 0.369\),\(p(1-p)\approx 0.233\);一等舱女性 \(p\approx 0.968\),\(p(1-p)\approx 0.031\)——两组方差相差约 7.5 倍,而样本量只差约 30%(122 人对 94 人),标准误相除约得 2.4 倍,所以样本更多的男性组误差线反而明显更长。对“读图下结论”的提醒:柱高接近 0 或 1 的组区间天然短、接近 0.5 的组天然长,误差线长短不能直接当“样本多不多”或“结论可不可靠”读;报告分组比例时应同时给出样本量与置信区间,差异是否显著要看区间是否重叠、必要时做正式的统计检验,而不是只比柱高。
# 验证脚本:一等舱分性别的人数、生还率与比例标准误 import numpy as np # 导入NumPy用于开方运算 import seaborn as sns # 导入Seaborn用于加载内置数据集 titanic_df = sns.load_dataset('titanic') # 加载泰坦尼克数据集(首次运行需联网) first_class_df = titanic_df[titanic_df['class'] == 'First'] # 取一等舱乘客子集 group_stat = first_class_df.groupby('sex', observed=True)['survived'].agg(['count', 'mean']) # 各性别人数与生还率 group_stat['std_error'] = np.sqrt(group_stat['mean'] * (1 - group_stat['mean']) / group_stat['count']) # 比例标准误sqrt(p(1-p)/n) print(group_stat.round(3)) # 保留三位小数输出 print('男性与女性标准误之比: {:.2f}'.format(group_stat.loc['male', 'std_error'] / group_stat.loc['female', 'std_error'])) # 两组误差线相对长短预期输出(本机 peter 环境实际运行结果,titanic 首次加载需联网,具体以平台运行结果为准):
count mean std_error sex female 94 0.968 0.018 male 122 0.369 0.044 男性与女性标准误之比: 2.41本机固定随机种子的实测中,平台任务同款
catplot图上一等舱男性误差线半长约 0.086、女性约 0.032,与正态近似 \(1.96\times\) 标准误(0.086 与 0.035)基本一致(自助抽样每次运行略有波动)。回扣本章:对应本章小结“易错点”第 5 条——柱状图误差线是置信区间,长短同时取决于样本量与该组比例的方差 \(p(1-p)\),样本多的组误差线未必更短,解读时不能只看柱高。